home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
rpg
/
crossfir.92
/
crossfir
/
crossfire-0.92.5
/
server
/
player.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-24
|
76KB
|
2,664 lines
/*
* static char *rcsid_player_c =
* "$Id: player.c,v 1.56 1996/07/24 07:40:18 master Exp master $";
*/
/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 1994 Mark Wedel
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author can be reached via e-mail to master@rahul.net
*/
#include <pwd.h>
#include <global.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <player.h>
#include <sounds.h>
#include <living.h>
#include <object.h>
#include <spells.h>
#include <skills.h>
#include <newclient.h>
/* This function returns TRUE if the object should be shown in the
* inventory window (due to show_what flags and object status). This
* should be a macro.
* By putting this in a function, it makes it easier to add new display
* types, and eliminates repetitive code.
*
* This function will only be used in the new client once all the
* old X11 stuff in the server is removed.
*/
int show_what_object(object *tmp, int show_what)
{
if (tmp->invisible) return 0;
switch (show_what) {
case show_all:
return 1;
case show_applied:
if (QUERY_FLAG(tmp, FLAG_APPLIED)) return 1;
else return 0;
case show_unapplied:
if (QUERY_FLAG(tmp, FLAG_APPLIED)) return 0;
else return 1;
case show_unpaid:
if (QUERY_FLAG(tmp, FLAG_UNPAID)) return 1;
else return 0;
case show_cursed:
if (QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)) return 1;
else return 0;
case show_magical:
case show_nonmagical: {
int status = QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL) ||
(QUERY_FLAG(tmp, FLAG_IDENTIFIED) && is_magical(tmp));
return (show_what==show_magical? status : !status);
}
default:
LOG(llevError,"show_what_object: unknown show_what state: %d\n",
show_what);
}
return 0;
}
void display_motd(object *op) {
#ifdef MOTD
char buf[MAX_BUF];
FILE *fp;
int comp;
sprintf(buf,"%s/%s",LibDir,MOTD);
if((fp=open_and_uncompress(buf,0,&comp))==NULL) {
return;
}
while(fgets(buf,MAX_BUF,fp)!=NULL) {
char *cp;
if(*buf=='#')
continue;
cp=strchr(buf,'\n');
if(cp!=NULL)
*cp='\0';
new_draw_info(NDI_UNIQUE, 0,op,buf);
}
close_and_delete(fp, comp);
new_draw_info(NDI_UNIQUE, 0,op," ");
#endif
}
int playername_ok(char *cp) {
for(;*cp!='\0';cp++)
if(!((*cp>='a'&&*cp<='z')||(*cp>='A'&&*cp<='Z'))&&*cp!='-'&&*cp!='_')
return 0;
return 1;
}
int add_player(char *disp, char *username, mapstruct *m, int esrv_client) {
player *p;
char buf[MAX_BUF],*cp,*defname = "nobody";
struct passwd *pwent;
if (username == NULL) {
pwent = getpwuid(getuid());
if (pwent)
username = pwent->pw_name;
else
username = defname;
}
/* Check for banned players and sites */
if (checkbanned (username, disp)){
fprintf (logfile, "Banned player tryed to add. [%s@%s]\n", username, disp);
fflush (logfile);
return 0;
}
init_beforeplay(); /* Make sure everything is ready */
if(m == NULL)
m = ready_map_name(first_map_path,0);
if(m == NULL)
fatal(MAP_ERROR);
m->timeout = MAP_TIMEOUT(m); /* Just in case we fail to add the player */
p=get_player_ob();
p->eric_server = esrv_client;
p->name=add_string(disp);
p->username = strdup_local(username);
p->writing=0,
p->weapon_sp=0,p->last_weapon_sp= -1;
p->last_armour= (-1);
p->has_hit=0;
p->scroll_inv=0;
p->scroll_look=0;
if (p->show_inv_icon)
strcpy(p->format_inv,"%-20.20s%-6s"); /* This can be changed by resize */
else
strcpy(p->format_inv,"%-24.24s%-6s"); /* This can be changed by resize */
strcpy(p->format_look,"%-24.24s%-6s");
p->inv_size=INV_SIZE;
p->look_size=LOOK_SIZE;
p->inv_chars=30;
p->look_chars=30;
p->barlength_inv=BARLENGTH_INV;
p->barlength_look=BARLENGTH_LOOK;
p->last_scroll_inv=p->last_scroll_look=1,
p->scrollsize_inv= p->scrollsize_look= p->scrollsize_hp=p->scrollsize_sp=
p->scrollsize_food=p->scrollstart_inv= p->scrollsize_grace=p->scrollstart_look=0,
p->nrofdrawn_inv=0,p->nrofdrawn_look=0;
p->peaceful=1; /* default peaceful */
p->do_los=1;
p->scroll=0; /* default without scroll */
p->split_window=default_split_window; /* See -w flag */
p->last_weight= -1;
p->freeze_inv=p->freeze_look=0;
p->viewmap=0;
p->mapres=1;
p->mapdelx[0]= -1;
#ifdef EXPLORE_MODE
p->explore=0;
#endif
p->menu = NULL;
if(use_pixmaps)
p->use_pixmaps = 1;
if (color_pix) {
p->use_pixmaps = 1;
p->color_pixmaps = 1;
}
if (esrv_client>0) {
p->gdisp = NULL;
load_default_keys(p);
p->gdisp = NULL;
} else {
strncpy(buf,disp,MAX_BUF);
if(strchr(buf,':')==NULL)
strcat(buf,":0");
if(get_root_display(p,buf) || get_game_display(p,buf) ||
get_stats_display(p,buf) || get_info_display(p,buf) ||
get_inv_display(p,buf) || get_look_display(p,buf) ||
get_message_display(p,buf)) {
free_player(p);
return 1;
}
setuperrors();
/* Is there any reason that the values checked for in the if statements
* need to be re-set below them? MSW (master@rahul.net)
*/
if (p->color_pixmaps) {
p->color_pixmaps = 1;
p->use_pixmaps = 1;
if (ReadPixmaps(p->gdisp, &p->pixmaps, &p->masks,&p->colormap)) {
if (!p->split_window)
XSetWindowColormap(p->gdisp, p->win_root, p->colormap);
XSetWindowColormap(p->gdisp, p->win_game, p->colormap);
XSetWindowColormap(p->gdisp, p->win_info, p->colormap);
XSetWindowColormap(p->gdisp, p->win_message, p->colormap);
XSetWindowColormap(p->gdisp, p->win_inv, p->colormap);
XSetWindowColormap(p->gdisp, p->win_look, p->colormap);
}
}
else if(p->use_pixmaps) {
p->use_pixmaps = 1;
p->pixmaps = ReadBitmaps (p->gdisp);
}
load_default_keys(p);
Protocol_atom = XInternAtom(p->gdisp, "WM_PROTOCOLS", False);
Kill_atom = XInternAtom(p->gdisp, "WM_DELETE_WINDOW", False);
if(!p->split_window)
XSetWMProtocols(p->gdisp, p->win_root, &Kill_atom, 1);
else { /* I hope this is correct... */
XSetWMProtocols(p->gdisp, p->win_game, &Kill_atom, 1);
XSetWMProtocols(p->gdisp, p->win_stats, &Kill_atom, 1);
XSetWMProtocols(p->gdisp, p->win_info, &Kill_atom, 1);
XSetWMProtocols(p->gdisp, p->win_inv, &Kill_atom, 1);
XSetWMProtocols(p->gdisp, p->win_look, &Kill_atom, 1);
XSetWMProtocols(p->gdisp, p->win_message, &Kill_atom, 1);
}
}
free_string(p->ob->name);
p->ob->name = NULL;
free_object(p->ob);
p->ob=get_player(p,m);
add_friendly_object(p->ob);
#ifdef MOTD
display_motd(p->ob);
#endif
if (esrv_client) {
/* These things get set below - set them here to default values for
* ericserver.
*/
#ifdef SOUND_EFFECTS
p->rplay_fd = 0;
#endif
p->peaceful=0;
p->berzerk=0;
p->scroll=0;
p->own_title[0]='\0';
get_name(p->ob);
} else {
if ((cp = XGetDefault(p->gdisp, name, "wimpy")) != NULL)
p->ob->run_away = atoi(cp);
#ifdef SOUND_EFFECTS
if ((cp = XGetDefault(p->gdisp, name, "sounds")) != NULL)
{
if (!strcmp("on", cp) || !strcmp("yes", cp))
p->rplay_fd = init_disp_sound(disp);
else if (!strcmp("off", cp) || !strcmp("no", cp))
p->rplay_fd = 0;
}
else
p->rplay_fd = init_disp_sound(disp);
LOG(llevDebug, "Rplay fd: %d\n", p->rplay_fd);
play_sound_player_only(p, SOUND_NEW_PLAYER);
#endif
if((cp=XGetDefault(p->gdisp,name,"name"))!=NULL&&check_name(p,cp)) {
if(p->ob->name!=NULL)
free_string(p->ob->name);
p->ob->name=add_string(cp);
remove_lock(p);
get_password(p->ob);
p->name_changed=1;
} else {
get_name(p->ob);
cp=strchr(buf,'.');
if(cp!=NULL) *cp='\0';
cp=strchr(buf,':');
if(cp!=NULL) *cp='\0';
p->ob->name=add_string(buf);
p->name_changed=0;
}
if((cp=XGetDefault(p->gdisp,name,"peaceful"))!=NULL)
if(!strcmp("on",cp)||!strcmp("yes",cp))
p->peaceful=1;
else if(!strcmp("off",cp)||!strcmp("no",cp))
p->peaceful=0;
if((cp=XGetDefault(p->gdisp,name,"berzerk"))!=NULL)
if(!strcmp("on",cp)||!strcmp("yes",cp))
p->berzerk=1;
else if(!strcmp("off",cp)||!strcmp("no",cp))
p->berzerk=0;
if((cp=XGetDefault(p->gdisp,name,"scroll"))!=NULL)
if(!strcmp("on",cp)||!strcmp("yes",cp))
p->scroll=1;
else if(!strcmp("off",cp)||!strcmp("no",cp))
p->scroll=0;
if((cp=XGetDefault(p->gdisp,name,"title"))!=NULL && strlen(cp)<MAX_NAME)
strcpy (p->own_title, cp);
else
p->own_title[0]='\0';
}
return 0;
}
/*
* get_player_archetype() return next player archetype from archetype
* list. Not very efficient routine, but used only creating new players.
* Note: there MUST be at least one player archetype!
*/
archetype *get_player_archetype(archetype* at)
{
archetype *start = at;
for (;;) {
if (at==NULL || at->next==NULL)
at=first_archetype;
else
at=at->next;
if(at->clone.type==PLAYER)
return at;
if (at == start) {
LOG (llevError, "No Player achetypes\n");
exit (-1);
}
}
}
object *get_player(player *p, mapstruct *m) {
object *op=arch_to_object(get_player_archetype(NULL));
int i;
p->loading = NULL;
op->map=m;
if(m->in_memory != MAP_IN_MEMORY) {
p->loading = m;
p->new_x = 0;
p->new_y = 0;
p->removed = 0;
op->x=0;
op->y=0;
} else {
i=find_free_spot(NULL,m,EXIT_X(m->map_object),EXIT_Y(m->map_object),
0,SIZEOFFREE);
op->x=EXIT_X(m->map_object)+freearr_x[i];
op->y=EXIT_Y(m->map_object)+freearr_y[i];
}
p->fire_on=0,p->run_on=0;
p->count=0;
p->count_left=0;
p->prev_cmd=' ';
p->prev_fire_on=0;
p->mode=0;
p->berzerk=1;
p->idle=0;
#ifdef AUTOSAVE
p->last_save_tick = 9999999;
#endif
*p->maplevel=0;
op->contr=p; /* this aren't yet in archetype */
op->speed_left=0.5;
op->speed=1.0;
op->direction=0;
op->stats.wc=2;
op->run_away = 25; /* Then we panick... */
roll_stats(op);
p->state=ST_ROLL_STAT;
clear_los(op);
p->last_stats.Str=0, p->last_stats.Dex=0,
p->last_stats.Int=0, p->last_stats.Con=0,
p->last_stats.Wis=0, p->last_stats.Cha=0;
p->last_stats.Pow=0, p->last_stats.Pow=0;
p->last_stats.hp=0, p->last_stats.maxhp=0;
p->last_stats.wc=0, p->last_stats.ac=0;
p->last_stats.sp=0, p->last_stats.maxsp=0;
p->last_stats.grace=0, p->last_stats.maxgrace=0;
p->last_stats.exp= -1,p->last_stats.food=0;
p->digestion=0,p->gen_hp=0,p->gen_sp=0,p->gen_grace=0;
p->last_spell= -1;
p->last_value= -1;
p->last_speed= -1;
p->shoottype=range_none,p->shootstrength=5;
p->last_shoot= range_bottom;
p->listening=9;
p->golem=NULL;
p->last_used=NULL;
p->last_used_id=0;
strncpy(p->title,op->arch->clone.name,MAX_NAME);
op->race = add_string (op->arch->clone.race);
(void)memset((void *)op->contr->drawn,'\0',
sizeof(Fontindex)*(WINRIGHT-WINLEFT+1)*(WINLOWER-WINUPPER+1));
for(i=0;i<NROFREALSPELLS;i++)
p->known_spells[i]= -1;
p->nrofknownspells=0;
p->chosen_spell = -1;
p->ob->chosen_skill = NULL;
#ifdef LINKED_SKILL_LIST
p->ob->sk_list = NULL;
#endif
if(QUERY_FLAG(op,FLAG_READY_SKILL))
CLEAR_FLAG(op,FLAG_READY_SKILL);
draw_all_inventory(op);
draw_all_look(op);
return op;
}
object *get_nearest_player(object *mon) {
object *op = NULL;
objectlink *ol;
int lastdist,tmp;
for(ol=first_friendly_object,lastdist=1000;ol!=NULL;ol=ol->next) {
if((ol->ob->type==PLAYER && ol->ob->contr->state)
||((ol->ob->invisible&&QUERY_FLAG(ol->ob,FLAG_UNDEAD)==QUERY_FLAG(mon,FLAG_UNDEAD))
&&!QUERY_FLAG(mon,FLAG_SEE_INVISIBLE))||ol->ob->map!=mon->map)
continue;
tmp=distance(ol->ob,mon);
if(lastdist>tmp) {
op=ol->ob;
lastdist=tmp;
}
}
return op;
}
int path_to_player(object *mon, object *pl,int mindiff) {
int dir,x,y,diff;
dir=find_dir_2(mon->x-pl->x,mon->y-pl->y);
x=mon->x+freearr_x[dir],y=mon->y+freearr_y[dir];
if(mon->value) x++;
diff=isqrt((x-pl->x)*(x-pl->x)+(y-pl->y)*(y-pl->y));
if(diff<mindiff) return 0;
while(diff-- > 0) {
if(blocked(mon->map,x,y))
return 0;
x+=freearr_x[dir],y+=freearr_y[dir];
}
return dir;
}
void give_initial_items(object *pl) {
object *op,*next=NULL;
static uint8 start_spells[] = {0, 1, 4, 5, 7};
static uint8 start_prayers[] = {19, 31, 32, 129};
pl->contr->freeze_inv=1;
if(pl->arch->randomitems!=NULL)
create_treasure(pl->arch->randomitems,pl,GT_INVENTORY,1,0);
op=pl->inv;
while(op || next) {
next = op->below;
if(op->nrof<2 && op->type!=CONTAINER && op->type!=MONEY)
SET_FLAG(op,FLAG_STARTEQUIP);
if(op->type==SPELLBOOK) { /* fix spells for first level spells */
if(!strcmp(op->arch->name,"cleric_book"))
op->stats.sp=start_prayers[RANDOM()%(sizeof(start_prayers)/sizeof(uint8))];
else
op->stats.sp=start_spells[RANDOM()%sizeof(start_spells)];
}
/* Give starting characters identified, uncursed, and undamned
* items. Just don't identify gold or silver, or it won't be
* merged properly.
*/
if (need_identify(op)) {
SET_FLAG(op, FLAG_IDENTIFIED);
CLEAR_FLAG(op, FLAG_CURSED);
CLEAR_FLAG(op, FLAG_DAMNED);
}
#ifndef ALLOW_SKILLS /* no reason to start with these if no skills exist! */
if(op->type==SKILLSCROLL || op->type==SKILL) {
remove_ob(op);
free_object(op);
}
#endif
if(op->type==ABILITY) {
pl->contr->known_spells[pl->contr->nrofknownspells++]=op->stats.sp;
remove_ob(op);
free_object(op);
}
op = next;
}
pl->contr->freeze_inv=0;
}
void get_name(object *op) {
op->contr->write_buf[0]='\0';
op->contr->writing=0;
op->contr->state=ST_GET_NAME;
new_draw_info(NDI_UNIQUE, 0,op,"What is your name?");
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,0,":");
else
write_ch(op,':');
}
void get_password(object *op) {
op->contr->write_buf[0]='\0';
op->contr->writing=0;
op->contr->state=ST_GET_PASSWORD;
new_draw_info(NDI_UNIQUE, 0,op,"What is your password?");
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,CS_QUERY_HIDEINPUT, ":");
else {
write_ch(op,':');
op->contr->no_echo=1;
}
}
void play_again(object *op)
{
new_draw_info(NDI_UNIQUE, 0,op,"Do you want to play again (a/q)?");
op->contr->state=ST_PLAY_AGAIN;
remove_lock(op->contr);
if (op->contr->eric_server>0)
send_query(op->contr->eric_server, CS_QUERY_SINGLECHAR, "");
}
void confirm_password(object *op) {
op->contr->write_buf[0]='\0';
op->contr->writing=0;
op->contr->state=ST_CONFIRM_PASSWORD;
new_draw_info(NDI_UNIQUE, 0,op,"Please type your password again.");
if (op->contr->eric_server>0)
send_query(op->contr->eric_server, CS_QUERY_HIDEINPUT, ":");
else
write_ch(op,':');
op->contr->no_echo=1;
}
#ifdef SIMPLE_PARTY_SYSTEM
void get_party_password(object *op, int partyid) {
op->contr->write_buf[0]='\0';
op->contr->writing=0;
op->contr->state=ST_GET_PARTY_PASSWORD;
op->contr->party_number_to_join = partyid;
new_draw_info(NDI_UNIQUE, 0,op,"What is the password?");
if (op->contr->eric_server>0)
send_query(op->contr->eric_server, CS_QUERY_HIDEINPUT, ":");
else
write_ch(op,':');
}
#endif /* SIMPLE_PARTY_SYSTEM */
int roll_stat() {
int a[4],i,j,k;
for(i=0;i<4;i++)
a[i]=(int)RANDOM()%6+1;
for(i=0,j=0,k=7;i<4;i++)
if(a[i]<k)
k=a[i],j=i;
for(i=0,k=0;i<4;i++) {
if(i!=j)
k+=a[i];
}
return k;
}
void roll_stats(object *op) {
int sum=0;
do {
op->stats.Str=roll_stat();
op->stats.Dex=roll_stat();
op->stats.Int=roll_stat();
op->stats.Con=roll_stat();
op->stats.Wis=roll_stat();
op->stats.Pow=roll_stat();
op->stats.Cha=roll_stat();
sum=op->stats.Str+op->stats.Dex+op->stats.Int+
op->stats.Con+op->stats.Wis+op->stats.Pow+
op->stats.Cha;
} while(sum<82||sum>116);
#if defined( USE_SWAP_STATS) && defined(SORT_ROLLED_STATS)
/* Sort the stats so that rerolling is easier... */
{
int i = 0, j = 0;
int statsort[7];
statsort[0] = op->stats.Str;
statsort[1] = op->stats.Dex;
statsort[2] = op->stats.Int;
statsort[3] = op->stats.Con;
statsort[4] = op->stats.Wis;
statsort[5] = op->stats.Pow;
statsort[6] = op->stats.Cha;
/* a quick and dirty bubblesort? */
do {
if (statsort[i] < statsort[i + 1]) {
j = statsort[i];
statsort[i] = statsort[i + 1];
statsort[i + 1] = j;
i = 0;
} else {
i++;
}
} while (i < 6);
op->stats.Str = statsort[0];
op->stats.Dex = statsort[1];
op->stats.Con = statsort[2];
op->stats.Int = statsort[3];
op->stats.Wis = statsort[4];
op->stats.Pow = statsort[5];
op->stats.Cha = statsort[6];
}
#endif /* SWAP_STATS */
#if 1
op->contr->orig_stats.Str=op->stats.Str;
op->contr->orig_stats.Dex=op->stats.Dex;
op->contr->orig_stats.Int=op->stats.Int;
op->contr->orig_stats.Con=op->stats.Con;
op->contr->orig_stats.Wis=op->stats.Wis;
op->contr->orig_stats.Pow=op->stats.Pow;
op->contr->orig_stats.Cha=op->stats.Cha;
#endif
op->stats.hp= -10000;
op->level=0;
op->stats.exp=0;
op->stats.sp=0;
op->stats.grace=0;
op->stats.ac=0;
add_exp(op,0);
op->stats.sp=op->stats.maxsp;
op->stats.hp=op->stats.maxhp;
#ifndef ALLOW_SKILLS /* start grace at maxgrace if no skills */
op->stats.grace=op->stats.maxgrace;
#else
op->stats.grace=0;
#endif
op->contr->orig_stats=op->stats;
}
void Roll_Again(object *op)
{
#ifndef USE_SWAP_STATS
new_draw_info(NDI_UNIQUE, 0,op,"Roll again (y/n)? ");
#else
new_draw_info(NDI_UNIQUE, 0,op,"[y] to roll new stats [n] to use stats");
new_draw_info(NDI_UNIQUE, 0,op,"[1-7] [1-7] to swap stats.\n");
new_draw_info(NDI_UNIQUE, 0,op,"Roll again (y/n/1-7)? ");
#endif /* USE_SWAP_STATS */
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,CS_QUERY_SINGLECHAR,"");
}
void Swap_Stat(object *op,int Swap_Second)
{
#ifdef USE_SWAP_STATS
signed char tmp;
char buf[MAX_BUF];
if ( op->contr->Swap_First == -1 ) {
new_draw_info(NDI_UNIQUE, 0,op,"How the hell did you get here?!?!!!");
new_draw_info(NDI_UNIQUE, 0,op,"Error in Swap_Stat code,");
new_draw_info(NDI_UNIQUE, 0,op,"mail korg@rdt.monash.edu.au");
return;
}
tmp = get_attr_value(&op->contr->orig_stats, op->contr->Swap_First);
set_attr_value(&op->contr->orig_stats, op->contr->Swap_First,
get_attr_value(&op->contr->orig_stats, Swap_Second));
set_attr_value(&op->contr->orig_stats, Swap_Second, tmp);
sprintf(buf,"%s done\n", short_stat_name[Swap_Second]);
new_draw_info(NDI_UNIQUE, 0,op, buf);
op->stats.Str = op->contr->orig_stats.Str;
op->stats.Dex = op->contr->orig_stats.Dex;
op->stats.Con = op->contr->orig_stats.Con;
op->stats.Int = op->contr->orig_stats.Int;
op->stats.Wis = op->contr->orig_stats.Wis;
op->stats.Pow = op->contr->orig_stats.Pow;
op->stats.Cha = op->contr->orig_stats.Cha;
op->stats.hp= -10000;
op->level=0;
op->stats.exp=0;
op->stats.sp=0;
op->stats.grace=0;
op->stats.ac=0;
add_exp(op,0);
op->stats.sp=op->stats.maxsp;
#ifndef ALLOW_SKILLS
op->stats.grace=op->stats.maxgrace;
#else
op->stats.grace=0;
#endif
op->stats.hp=op->stats.maxhp;
add_exp(op,0);
op->contr->Swap_First=-1;
#endif /* USE_SWAP_STATS */
}
/* This code has been greatly reduced, because with set_attr_value
* and get_attr_value, the stats can be accessed just numeric
* ids. stat_trans is a table that translate the number entered
* into the actual stat. It is needed because the order the stats
* are displayed in the stat window is not the same as how
* the number's access that stat. The table does that translation.
*/
int key_roll_stat(object *op, char key)
{
int keynum = key -'0';
char buf[MAX_BUF];
static sint8 stat_trans[] = {-1, STR, DEX, CON, INT, WIS, POW, CHA};
#ifdef USE_SWAP_STATS
if (keynum>0 && keynum<=7) {
if (op->contr->Swap_First==-1) {
op->contr->Swap_First=stat_trans[keynum];
sprintf(buf,"%s ->", short_stat_name[stat_trans[keynum]]);
new_draw_info(NDI_UNIQUE, 0,op,buf);
}
else
Swap_Stat(op,stat_trans[keynum]);
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,CS_QUERY_SINGLECHAR,"");
return 1;
}
#endif
switch (key) {
case 'n':
case 'N':
SET_FLAG(op, FLAG_WIZ);
if(op->map==NULL) {
LOG(llevError,"Map == NULL in state 2\n");
break;
}
/* So that enter_exit will put us at startx/starty */
op->x= -1;
enter_exit(op,NULL);
/* Enter exit adds a player otherwise */
if(op->contr->loading == NULL) {
insert_ob_in_map(op,op->map);
}
else {
op->contr->removed = 0; /* Will insert pl. when map is loaded */
}
add_statbonus(op);
new_draw_info(NDI_UNIQUE, 0,op,"Now choose a character.");
new_draw_info(NDI_UNIQUE, 0,op,"Press any key to change outlook.");
new_draw_info(NDI_UNIQUE, 0,op,"Press `d' when you're pleased.");
op->contr->state = ST_CHANGE_CLASS;
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,CS_QUERY_SINGLECHAR,"");
return 0;
case 'y':
case 'Y':
roll_stats(op);
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,CS_QUERY_SINGLECHAR,"");
return 1;
case 'q':
case 'Q':
play_again(op);
return 1;
default:
#ifndef USE_SWAP_STATS
new_draw_info(NDI_UNIQUE, 0,op,"Yes, No or Quit. Roll again?");
#else
new_draw_info(NDI_UNIQUE, 0,op,"Yes, No, Quit or 1-6. Roll again?");
#endif /* USE_SWAP_STATS */
if (op->contr->eric_server>0)
send_query(op->contr->eric_server,CS_QUERY_SINGLECHAR,"");
return 0;
}
return 0;
}
/* This function takes the key that is passed, and does the
* appropriate action with it (change class, or other things.
*/
int key_change_class(object *op, char key)
{
int tmp_loop;
if(key=='q'||key=='Q') {
remove_ob(op);
play_again(op);
return 0;
}
if(key=='d'||key=='D') {
/* this must before then initial items are given */
if (op->contr->eric_server > 0)
esrv_new_player(op->contr->eric_server, op->count, op->name,
op->weight, op->face->number);
create_treasure(find_treasurelist("starting_wealth"),op, 0, 0, 0);
op->contr->state=ST_PLAYING;
#ifdef AUTOSAVE
op->contr->last_save_tick = pticks;
#endif
start_info(op);
CLEAR_FLAG(op, FLAG_WIZ);
#ifdef ALLOW_SKILLS
(void) init_player_exp(op);
#endif
give_initial_items(op);
#ifdef ALLOW_SKILLS
(void) link_player_skills(op);
#endif
if (op->contr->eric_server > 0)
esrv_send_inventory(op, op);
else
draw_all_inventory(op);
return 0;
}
/* Following actually changes the class - this is the default command
* if we don't match with one of the options above.
*/
tmp_loop = 0;
while(!tmp_loop) {
char *name = add_string (op->name);
int x = op->x, y = op->y;
remove_statbonus(op);
remove_ob (op);
op->arch = get_player_archetype(op->arch);
copy_object (&op->arch->clone, op);
op->stats = op->contr->orig_stats;
free_string (op->name);
op->name = name;
op->x = x;
op->y = y;
insert_ob_in_map (op, op->map);
strncpy(op->contr->title,op->arch->clone.name,MAX_NAME);
add_statbonus(op);
tmp_loop=allowed_class(op);
}
update_object(op);
fix_player(op);
op->stats.hp=op->stats.maxhp;
op->stats.sp=op->stats.maxsp;
#ifndef ALLOW_SKILLS
op->stats.grace=op->stats.maxgrace;
#else
op->stats.grace=0;
#endif
op->contr->last_value= -1;
draw_stats(op);
if (op->contr->eric_server>0)
send_query(op->contr->eric_server, CS_QUERY_SINGLECHAR,"");
return 0;
}
int key_confirm_quit(object *op, char key)
{
char buf[MAX_BUF];
if(key!='y'&&key!='Y'&&key!='q'&&key!='Q') {
op->contr->state=ST_PLAYING;
new_draw_info(NDI_UNIQUE, 0,op,"OK, continuing to play.");
return 1;
}
terminate_all_pets(op);
remove_ob(op);
op->direction=0;
op->contr->count_left=0;
op->map->players--;
/* Just in case we fail to add the player */
op->map->timeout = MAP_TIMEOUT(op->map);
new_draw_info_format(NDI_UNIQUE | NDI_ALL, 5, NULL,
"%s quits the game.",op->name);
strcpy(op->contr->killer,"quit");
check_score(op);
#ifdef SIMPLE_PARTY_SYSTEM
op->contr->party_number=(-1);
#endif /* SIMPLE_PARTY_SYSTEM */
#ifdef SET_TITLE
op->contr->own_title[0]='\0';
#endif /* SET_TITLE */
load_default_keys(op->contr);
if(!QUERY_FLAG(op,FLAG_WAS_WIZ)) {
sprintf(buf,"%s/%s/%s.pl",LibDir,PlayerDir,op->name);
if(unlink(buf)== -1 && debug)
perror("crossfire (delete character)");
}
play_again(op);
return 1;
}
void flee_player(object *op) {
int dir,diff;
if(op->stats.hp < 0) {
LOG(llevDebug, "Fleeing player is dead.\n");
CLEAR_FLAG(op, FLAG_SCARED);
return;
}
if(op->enemy==NULL) {
LOG(llevDebug,"Fleeing player had no enemy.\n");
CLEAR_FLAG(op, FLAG_SCARED);
return;
}
if(!(RANDOM()%5)&&RANDOM()%20+1>=savethrow[op->level]) {
op->enemy=NULL;
CLEAR_FLAG(op, FLAG_SCARED);
return;
}
dir=absdir(4+find_dir_2(op->x-op->enemy->x,op->y-op->enemy->y));
for(diff=0;diff<3;diff++) {
int m=1-(RANDOM()&2);
if(move_ob(op,absdir(dir+diff*m))||
(diff==0&&move_ob(op,absdir(dir-diff*m)))) {
draw(op);
return;
}
}
/* Cornered, get rid of scared */
CLEAR_FLAG(op, FLAG_SCARED);
op->enemy=NULL;
}
int check_pick(object *op) {
int item_picked=0; /* set to true if an item is picked up */
if(QUERY_FLAG(op,FLAG_FLYING) || op->below==NULL || !can_pick(op,op->below))
return 1;
#ifdef SEARCH_ITEMS
if(op->contr->search_str[0]!='\0')
{
object *next,*tmp;
tmp=op->below;
while(tmp!=NULL&&can_pick(op,tmp))
{
next=tmp->below;
if(strstr(long_desc(tmp),op->contr->search_str)!=NULL) {
pick_up(op,tmp);
item_picked=1;
}
tmp=next;
}
}
#endif /* SEARCH_ITEMS */
switch (op->contr->mode) {
case 0: return 1; /* don't pick up */
case 1:
pick_up(op,op->below);
return 1;
case 2:
pick_up(op,op->below);
return 0;
case 3: return 0; /* stop before pickup */
case 4:
case 5:
case 6:
case 7: {
object *item,*temp;
item = op->below;
op->contr->freeze_inv=1;
while (item) {
temp = item->below;
if (can_pick(op, item)) {
if (op->contr->mode==6) {
if (QUERY_FLAG(item, FLAG_KNOWN_MAGICAL) &&
!QUERY_FLAG(item, FLAG_KNOWN_CURSED)) {
pick_up(op, item);
item_picked=1;
}
}
else if (op->contr->mode==7) {
if (item->type==MONEY || item->type==GEM) {
item_picked=1;
pick_up(op, item);
}
} else {
item_picked=1;
pick_up(op, item);
}
}
item = temp;
}
op->contr->freeze_inv=0;
if (item_picked && !op->contr->eric_server)
draw_inventory(op);
return (op->contr->mode != 4 ? 1 : 0);
}
/* use value density */
default: {
object * item,*temp;
item=op->below;
op->contr->freeze_inv=1;
while(item) {
temp=item->below;
if(can_pick(op, item) && !(QUERY_FLAG(item, FLAG_UNPAID)) &&
(query_cost(item,op,F_TRUE)*100/
(item->weight * MAX(item->nrof,1))>= op->contr->mode) ) {
item_picked=1;
pick_up(op,item);
}
item=temp;
}
op->contr->freeze_inv=0;
if (item_picked && !op->contr->eric_server)
draw_inventory(op);
return 1;
}
}
return 1; /* Statement supposedly can't be reached */
}
/*
* Find an arrow in the inventory and after that
* in the right type container (quiver). Pointer to the
* found object is returned.
*/
object *find_arrow(object *op, char *type)
{
object *tmp = NULL;
for(op=op->inv; op; op=op->below)
if(!tmp && op->type==CONTAINER && op->race==type &&
QUERY_FLAG(op,FLAG_APPLIED))
tmp = find_arrow (op, type);
else if (op->type==ARROW && op->race==type)
return op;
return tmp;
}
/*
* Player fires a bow. This probably should be combined with
* monster_use_bow().
*/
static void fire_bow(object *op, int dir)
{
object *bow, *arrow = NULL, *left;
for(bow=op->inv; bow; bow=bow->below)
if(bow->type==BOW && QUERY_FLAG(bow, FLAG_APPLIED))
break;
#ifdef MANY_CORES /* there must be applied bow if shoot_type is range_bow */
if (!bow) {
LOG (llevError, "Range: bow without activated bow.\n");
abort();
}
#endif
if( !bow->race ) {
sprintf (errmsg, "Your %s is broken.", bow->name);
new_draw_info(NDI_UNIQUE, 0,op, errmsg);
op->contr->count_left=0;
return;
}
if ((arrow=find_arrow(op, bow->race)) == NULL) {
sprintf (errmsg, "You have no %s left.", bow->race);
new_draw_info(NDI_UNIQUE, 0,op,errmsg);
op->contr->count_left=0;
return;
}
if(wall(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) {
new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way.");
op->contr->count_left=0;
return;
}
/* this should not happen, but sometimes does */
if (arrow->nrof==0) {
remove_ob(arrow);
free_object(arrow);
return;
}
left = arrow; /* these are arrows left to the player */
arrow = get_split_ob(arrow, 1);
set_owner(arrow,op);
arrow->direction=dir;
arrow->x = op->x;
arrow->y = op->y;
arrow->speed = 1;
op->speed_left = 0.01 - (float) FABS(op->speed) * 100 / bow->stats.sp;
fix_player(op);
update_ob_speed(arrow);
arrow->speed_left = 0;
arrow->face = &new_faces[arrow->arch->faces[dir]];
arrow->stats.sp = arrow->stats.wc; /* save original wc and dam */
arrow->stats.hp = arrow->stats.dam;
arrow->stats.dam += (QUERY_FLAG(bow, FLAG_NO_STRENGTH) ?
0 : dam_bonus[op->stats.Str]) +
bow->stats.dam + bow->magic + arrow->magic;
arrow->stats.wc = 20 - bow->magic - arrow->magic - op->level -
dex_bonus[op->stats.Dex] - thaco_bonus[op->stats.Str] - arrow->stats.wc -
bow->stats.wc;
arrow->map = op->map;
SET_FLAG(arrow, FLAG_FLYING);
SET_FLAG(arrow, FLAG_FLY_ON);
SET_FLAG(arrow, FLAG_WALK_ON);
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_FIRE_ARROW);
#endif
D_LOCK(op);
insert_ob_in_map(arrow,op->map);
move_arrow(arrow);
D_UNLOCK(op);
if (op->contr->eric_server > 0) {
if (QUERY_FLAG(left, FLAG_FREED))
esrv_del_item(op->contr->eric_server, left->count);
else
esrv_send_item(op, left);
}
}
void fire(object *op,int dir) {
object *weap=NULL;
int spellcost=0;
/* op->hide needs to be checked here -b.t.*/
if (op->contr->tmp_invis && !op->hide) { /* tmp invis goes away now */
op->invisible = 0;
op->contr->tmp_invis = 0;
update_object(op);
}
/* a check for players, make sure things are groovy. This routine
* will change the skill of the player as appropriate in order to
* fire whatever is requested. In the case of spells (range_magic)
* it handles whether cleric or mage spell is requested to be cast.
* -b.t.
*/
#ifdef ALLOW_SKILLS
if(op->type==PLAYER&&!QUERY_FLAG(op,FLAG_WIZ))
if(!check_skill_to_fire(op)) return;
#endif
switch(op->contr->shoottype) {
case range_none:
return;
case range_bow:
fire_bow(op, dir);
return;
case range_magic: /* Casting spells */
op->contr->shoottype= range_magic;
spellcost=cast_spell(op,op,dir,op->contr->chosen_spell,0,spellNormal,NULL);
if(spells[op->contr->chosen_spell].cleric)
op->stats.grace-=spellcost;
else
op->stats.sp-=spellcost;
draw_stats(op);
return;
case range_wand:
for(weap=op->inv;weap!=NULL;weap=weap->below)
if(weap->type==WAND&&QUERY_FLAG(weap, FLAG_APPLIED))
break;
if(weap==NULL) {
new_draw_info(NDI_UNIQUE, 0,op,"You have no wand readied.");
op->contr->count_left=0;
return;
}
if(weap->stats.food<=0) {
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_WAND_POOF);
#endif
new_draw_info(NDI_UNIQUE, 0,op,"The wand says poof.");
return;
}
if(cast_spell(op,weap,dir,op->contr->chosen_item_spell,0,spellWand,NULL)) {
SET_FLAG(op, FLAG_BEEN_APPLIED); /* You now know something about it */
if (!(--weap->stats.food))
{
object *tmp;
if (weap->arch) {
CLEAR_FLAG(weap, FLAG_ANIMATE);
weap->face = weap->arch->clone.face;
weap->speed = 0;
update_ob_speed(weap);
}
if ((tmp=is_player_inv(weap)))
draw_inventory_faces(tmp);
}
}
return;
case range_rod:
case range_horn:
for(weap=op->inv;weap!=NULL;weap=weap->below)
if(QUERY_FLAG(weap, FLAG_APPLIED)&&
weap->type==(op->contr->shoottype==range_rod?ROD:HORN))
break;
if(weap==NULL) {
char buf[MAX_BUF];
sprintf(buf, "You have no %s readied.",
op->contr->shoottype == range_rod ? "rod" : "horn");
new_draw_info(NDI_UNIQUE, 0,op, buf);
op->contr->count_left=0;
return;
}
if(weap->stats.hp<spells[weap->stats.sp].sp) {
LOG(llevDebug,"Horn/rod: %d < %d (%d)\n", weap->stats.hp, spells[weap->stats.sp].sp, weap->stats.sp);
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_WAND_POOF);
#endif
if (op->contr->shoottype == range_rod)
new_draw_info(NDI_UNIQUE, 0,op,"The rod whines for a while, but nothing happens.");
else
new_draw_info(NDI_UNIQUE, 0,op,
"No matter how hard you try you can't get another note out.");
return;
}
if(cast_spell(op,weap,dir,op->contr->chosen_item_spell,0,
op->contr->shoottype == range_rod ? spellRod : spellHorn,NULL)) {
SET_FLAG(op, FLAG_BEEN_APPLIED); /* You now know something about it */
drain_rod_charge(weap);
}
return;
case range_scroll: /* Control summoned monsters from scrolls */
if(op->contr->golem==NULL) {
op->contr->shoottype=range_none;
op->contr->chosen_spell= -1;
draw_stats(op);
}
else
control_golem(op->contr->golem, dir);
return;
case range_skill:
if(!op->chosen_skill) {
if(op->type==PLAYER)
new_draw_info(NDI_UNIQUE, 0,op,"You have no applicable skill to use.");
return;
}
(void) do_skill(op,dir,NULL);
return;
#if 0
case 10: /* Thrown items */
new_draw_info(NDI_UNIQUE, 0,op,"Throw is unimplemented as of now.");
return;
tmp=op->inv;
if(tmp==NULL) {
new_draw_info(NDI_UNIQUE, 0,op,"You have nothing to throw.");
op->contr->count_left=0;
return;
}
if((tmp->weight/1000)>max_carry[op->stats.Str]/2) {
sprintf(buf,"You're not strong enough to throw %s.",tmp->name);
new_draw_info(NDI_UNIQUE, 0,op,buf);
op->contr->count_left=0;
return;
}
if(tmp->type!=BOMB&&(tmp->speed||IS_ALIVE(tmp))) {
sprintf(buf,"You can only throw dead objects, not the %s.",tmp->name);
new_draw_info(NDI_UNIQUE, 0,op,buf);
op->contr->count_left=0;
return;
}
if(tmp->type!=BOMB)
update_ob_speed(tmp,0.5);
tmp->direction=dir;
tmp->thrown=max_carry[op->stats.Str]/(tmp->weight/500);
if(tmp->thrown<1) tmp->thrown=1;
if(tmp->thrown>10) tmp->thrown=10;
tmp->thrownthaco=
20-op->level-dex_bonus[op->stats.Dex]-thaco_bonus[op->stats.Str];
set_owner(tmp,op);
move_thrown(tmp);
return;
#endif
default:
new_draw_info(NDI_UNIQUE, 0,op,"Illegal shoot type.");
op->contr->count_left=0;
return;
}
}
/* This function is just part of a breakup from move_player.
* It should keep the code cleaner.
* When this is called, the players direction has been updated
* (taking into accoutn confusion.) The player is also actually
* going to try and move (not fire weapons).
*/
void move_player_attack(object *op, int dir)
{
object *tmp, *tmp2;
int nx=freearr_x[dir]+op->x,ny=freearr_y[dir]+op->y;
/* If braced, or can't move to the square, and it is not out of the
* map, attack it. Note order of if statement is important - don't
* want to be calling move_ob if braced, because move_ob will move the
* player. This is a pretty nasty hack, because if we could
* move to some space, it then means that if we are braced, we should
* do nothing at all. As it is, if we are braced, we go through
* quite a bit of processing. However, it probably is less than what
* move_ob uses.
*/
if ((op->contr->braced || !move_ob(op,dir)) &&
!out_of_map(op->map,nx,ny)) {
op->contr->has_hit = 1; /* The last action was to hit, so use weapon_sp */
if ((tmp=get_map_ob(op->map,nx,ny))==NULL) {
/* LOG(llevError,"player_move_attack: get_map_ob returns NULL, but player can not more there.\n");*/
return;
}
/* Go through all the objects, and stop if we find one of interest. */
while (tmp->above!=NULL) {
if ((QUERY_FLAG(tmp,FLAG_ALIVE) || QUERY_FLAG(tmp,FLAG_CAN_ROLL)
|| tmp->type ==LOCKED_DOOR) && tmp!=op)
break;
tmp=tmp->above;
}
if (tmp==NULL) /* This happens anytime the player tries to move */
return; /* into a wall */
if(tmp->head != NULL)
tmp = tmp->head;
/* This blocks deals with opening a normal door. We look for a key,
* and if we found one, break the door. If not, let normal attack
* code deal with it.
*/
if (tmp->type==DOOR && tmp->stats.hp>=0) {
tmp2=op->inv;
while(tmp2!=NULL&&tmp2->type!=KEY) /* Find a key */
tmp2=tmp2->below;
if(tmp2!=NULL) { /* we found a key */
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_OPEN_DOOR);
#endif
decrease_ob(tmp2); /* Use up one of the keys */
hit_player(tmp,9999,op,AT_PHYSICAL); /* Break through the door */
if(tmp->inv && tmp->inv->type ==RUNE) spring_trap(tmp->inv,op);
}
}
/* This area deals with locked doors. These are doors that require
* special keys.
*/
if(tmp->type==LOCKED_DOOR) {
tmp2=op->inv;
while(tmp2 && (tmp2->type != SPECIAL_KEY ||
tmp2->slaying != tmp->slaying)) /* Find the key */
tmp2=tmp2->below;
if(tmp2) {
decrease_ob_nr(tmp2, 1); /* Use the key */
remove_door2(tmp); /* remove door without violence ;-) */
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_OPEN_DOOR);
#endif
} else if (tmp->msg) /* show door's message if present */
new_draw_info(NDI_UNIQUE | NDI_NAVY, 0, op, tmp->msg);
}
/* The following deals with possibly attacking peaceful
* or frienddly creatures. Basically, all players are considered
* unaggressive. If the moving player has peaceful set, then the
* object should be pushed instead of attacked. It is assumed that
* if you are braced, you will not attack friends accidently,
* and thus will not push them.
*/
if (tmp->enemy != op &&
(tmp->type==PLAYER || QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE)
|| QUERY_FLAG(tmp, FLAG_FRIENDLY)) &&
op->contr->peaceful && (!op->contr->braced)) {
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_PUSH_PLAYER);
#endif
(void) push_ob(tmp,dir,op);
}
/* If the object is a boulder or other rollable object, then
* roll it if not braced. You can't roll it if you are braced.
*/
else if(QUERY_FLAG(tmp,FLAG_CAN_ROLL)&&(!op->contr->braced))
recursive_roll(tmp,dir,op);
/* Any generic living creature. Including things like doors.
* Way it works is like this: First, it must have some hit points
* and be living. Then, it must be one of the following:
* 1) Not a player, 2) A player, but of a different party. Note
* that party_number -1 is no party, so attacks can still happen.
*/
else
#ifdef SIMPLE_PARTY_SYSTEM
if ((tmp->stats.hp>=0) && QUERY_FLAG(tmp, FLAG_ALIVE) &&
((tmp->type!=PLAYER || op->contr->party_number==-1 ||
op->contr->party_number!=tmp->contr->party_number))) {
#else
if ((tmp->stats.hp>=0) && QUERY_FLAG(tmp, FLAG_ALIVE)) {
#endif
#ifdef ALLOW_SKILLS
skill_attack(tmp, op, 0, NULL);
#else
(void) attack_ob(tmp, op);
#endif
/* If attacking another player, that player gets automatic
* hitback, and doesn't loose luck either.
*/
if (tmp->type == PLAYER && tmp->stats.hp >= 0 &&
!tmp->contr->has_hit)
{
short luck = tmp->stats.luck;
tmp->contr->has_hit = 1;
#ifdef ALLOW_SKILLS
skill_attack(op, tmp, 0, NULL);
#else
(void) attack_ob(op, tmp);
#endif
tmp->stats.luck = luck;
}
if(op->contr->tmp_invis) {
op->contr->tmp_invis=0;
op->invisible=0;
op->hide=0;
update_object(op);
}
}
}
}
int move_player(object *op,int dir) {
int num1;
int face = dir ? (dir - 1) / 2 : -1;
if(op->map == NULL || op->map->in_memory != MAP_IN_MEMORY)
return 0;
/* peterm: added following line */
op->facing = dir;
if(QUERY_FLAG(op,FLAG_CONFUSED) && dir)
dir = absdir(dir + RANDOM()%3 + RANDOM()%3 - 2);
/* For Hidden players- a chance of becoming 'unhidden'
* every time they move - as we subtract off 'invisibility'
* from the player bt. (thomas@astro.psu.edu)
*/
if(op->hide>0) {
num1=RANDOM()%10 + op->map->difficulty;
if(num1>op->invisible) {
char buf[MAX_BUF];
op->contr->tmp_invis=0;
op->hide=0;
op->invisible=0;
sprintf(buf,"You moved too much! You are visible!");
new_draw_info(NDI_UNIQUE, 0,op,buf);
} else
op->invisible-=num1;
}
if(op->contr->fire_on)
fire(op,dir);
else move_player_attack(op,dir);
if((check_pick(op)&&op->contr->run_on)||
(op->contr->berzerk&&op->contr->run_on))
op->direction=dir;
else
op->direction=0;
if(face != -1)
op->face = &new_faces[op->arch->faces[face]];
update_object(op);
return 0;
}
int has_cleared_window(Window w,Window *win_clr,int max) {
for(max--;max>=0;max--)
if(win_clr[max]==w)
return 1;
return 0;
}
/* splitting this out created a 292 line function !!!!!!*/
/* Returns whether or not to keep processing events */
int handle_buttonpress(object *op)
{
/* Need some more local variables */
int i=0,y=op->contr->gevent.xbutton.y,x=op->contr->gevent.xbutton.x,
button=op->contr->gevent.xbutton.button;
int dx=(x-2)/24-5,dy=(y-2)/24-5;
object *inv;
inv = op->inv;
if(op->contr->state||QUERY_FLAG(op,FLAG_REMOVED))
return 1;
if(op->above) {
remove_ob(op);
insert_ob_in_map(op,op->map);
}
if(op->contr->gevent.xbutton.window==op->contr->win_game) {
switch (button) {
case 1:
{
if(dx<WINLEFT||dx>WINRIGHT||dy<WINUPPER||dy>WINLOWER)
return 1;
if(op->contr->blocked_los[dx+5][dy+5])
return 1;
look_at(op,dx,dy);
op->contr->last_cmd=(dx||dy)?0:2;
}
return 1;
case 2:
case 3:
if (button == 2)
op->contr->fire_on=1;
if (x<115)
i = 0;
else if (x>149)
i = 6;
else i =3;
if (y>152)
i += 2;
else if (y>115)
i++;
switch (i) {
case 0: move_player (op,8);break;
case 1: move_player (op,7);break;
case 2: move_player (op,6);break;
case 3: move_player (op,1);break;
case 5: move_player (op,5);break;
case 6: move_player (op,2);break;
case 7: move_player (op,3);break;
case 8: move_player (op,4);break;
}
if (button == 2)
op->contr->fire_on=0;
if (i>=0 && i<=8) return 1;
return 0;
}
return 1;
} else if(op->contr->gevent.xbutton.window == op->contr->win_message) {
#ifdef USE_BUTTONS
if (y>62) {
extern int opX [NUMOPBUTTS];
for (i=0; i< NUMOPBUTTS+1; i++) {
if (x>opX[i]&&x<opX[i]+64&&y>opY[i]&&y<opY[i]+18) {
if (i == CHANGE_button) {
if (button == 1)
change_spell (op,'+');
else if (button == 2)
change_spell (op,'-');
} else if (i == APPLY_button) {
if (button == 1)
apply_below (op);
else if (button ==2) {
apply_inventory(op);
if (op->contr->show_what == show_applied ||
op->contr->show_what == show_unapplied)
draw_inventory(op);
}
} else if (i == TALK_button)
;
else if (i == PEACE_button) {
char *pbuf="peaceful"; /*parse_string needs a writable string */
parse_string (op,pbuf);
}
}
}
} else {
for (i=0;i<NUMDIRBUTTS+1;i++)
if(x>dirX[i]&&x<dirX[i]+dirW&&y>dirY[i]&&y<dirY[i]+18) {
if (button == 2)
op->contr->fire_on=1;
else if (button == 3)
op->contr->run_on=1;
switch (i) {
case NW_button: move_player (op,8);return 1;
case NO_button: move_player (op,1);return 1;
case NE_button: move_player (op,2);return 1;
case WE_button: move_player (op,7);return 1;
case BR_button: return 1;
case EA_button: move_player (op,3);return 1;
case SW_button: move_player (op,6);return 1;
case SO_button: move_player (op,5);return 1;
case SE_button: move_player (op,4);return 1;
default:return 1;
}
op->contr->fire_on=0;
op->contr->run_on=0;
}
}
#else
return 1;
#endif
} else if(op->contr->gevent.xbutton.window==op->contr->win_inv) {
int dy=(y-16)/24+op->contr->scroll_inv;
object *tmp;
if(x>270) {
dy-=op->contr->scroll_inv;
switch(button) {
case 1:
op->contr->scroll_inv-=dy>0?dy:1;
draw_inventory(op);
return 1;
case 2:
{
int objects,scroll;
object *tmp;
for(tmp=inv,objects=0;tmp!=NULL;tmp=tmp->below)
if (show_what_object(tmp, op->contr->show_what))
objects++;
scroll=(y-17)*objects/op->contr->barlength_inv;
if(op->contr->scroll_inv==scroll)
return 1;
op->contr->scroll_inv=scroll;
draw_inventory(op);
return 1;
}
case 3:
op->contr->scroll_inv+=dy>0?dy:1;
draw_inventory(op);
return 1;
}
return 1;
}
if(dy<0)
return 1;
for(tmp=inv,i=0;tmp!=NULL;tmp=tmp->below) {
while(tmp != NULL && (!show_what_object(tmp, op->contr->show_what)))
tmp = tmp->below;
if (tmp == NULL)
return 1;
if (i++ == dy) {
switch(button) {
case 1:
if(op->contr->gevent.xbutton.state == ShiftMask) {
if(QUERY_FLAG(tmp,FLAG_INV_LOCKED))
unlock_inv(op,tmp);
else
lock_inv(op,tmp);
}
else
examine(op,tmp);
return 1;
case 2:
apply(op,tmp);
if (op->contr->show_what == show_applied || op->contr->show_what==show_unapplied)
draw_inventory(op);
return 1;
case 3:
if(tmp->type==CONTAINER && QUERY_FLAG(tmp,FLAG_APPLIED)) {
if(op->container==tmp) return 1; /* fix an endless loop problem */
for(inv=tmp->inv;inv!=NULL;) {
/* The drop may fail if dropping from container TO container */
tmp=inv->below;
drop(op,inv);
inv=tmp;
}
}
else drop(op,tmp);
return 1;
}
return 1;
}
}
return 1;
} else if(op->contr->gevent.xbutton.window==op->contr->win_look) {
int dy=(y-16)/24+op->contr->scroll_look;
object *top, *tmp;
if(x>270) {
dy-=op->contr->scroll_look;
switch(button) {
case 1:
op->contr->scroll_look-=dy>0?dy:1;
draw_look(op);
return 1;
case 2:
{
int objects,scroll;
/* Eneq(@csd.uu.se): ... */
for(tmp=(op->container?op->container->inv:op->below),objects=0;tmp!=NULL;tmp=tmp->below)
if(!(tmp->invisible))
objects++;
scroll=(y-17)*objects/op->contr->barlength_look;
if(op->contr->scroll_look==scroll)
return 1;
op->contr->scroll_look=scroll;
draw_look(op);
return 1;
}
case 3:
op->contr->scroll_look+=dy>0?dy:1;
draw_look(op);
return 1;
}
return 1;
}
if(dy<0)
return 1;
if(wall(op->map,op->x,op->y))
return 1;
/* Eneq(@csd.uu.se): Altered container-treatment, displays
contents in look-window. */
if (op->container)
top=op->container->inv;
else
for(top=get_map_ob(op->map,op->x,op->y);
top!=NULL&&top->above!=NULL&&top->above!=op;top=top->above);
/* Moved i<dy out of for loop. This is because with it in
* the for loop, a check to see if it is a valid item for the
* look window is never made. master@rahul.net
*/
for(tmp=top,i=0;(tmp!=NULL)&&!(tmp->above&&
QUERY_FLAG(tmp->above,FLAG_IS_FLOOR))
;tmp=tmp->below)
if (LOOK_OBJ(tmp)) {
i++;
if (i>dy) break;
}
if(i<=dy) /* means we did not find enough valid objects */
return 1;
switch(button) {
case 1:
examine(op,tmp);
return 1;
case 2:
apply(op,tmp);
draw_look(op);
return 1;
case 3:
pick_up(op,tmp);
return 1;
}
return 1;
} else if(op->contr->gevent.xbutton.window==op->contr->win_info) {
object *tmp;
switch(button) {
case 1:
case 2:
if(op->contr->last_cmd!=1) {
inventory(op,NULL);
op->contr->last_cmd=1;
return 1;
}
for(tmp=inv,i=0;tmp!=NULL&&i<(y-13)/13;tmp=tmp->below,i++);
if(tmp!=NULL) {
if(button==1)
apply(op,tmp);
else
drop(op,tmp);
inventory(op,NULL);
op->contr->last_cmd=1;
}
return 1;
case 3:
if(op->contr->last_cmd!=2) {
look_at(op,0,0);
op->contr->last_cmd=2;
return 1;
}
if(wall(op->map,op->x,op->y))
return 1;
for(tmp=op->below,i=0;tmp!=NULL&&
(QUERY_FLAG(op,FLAG_WIZ)||(tmp->type!=EARTHWALL&&tmp!=op))&&i<(y-13)/13;
tmp=tmp->below,i++);
if(tmp!=NULL) {
pick_up(op,tmp);
op->contr->last_cmd=0;
}
return 1;
default:
new_draw_info(NDI_UNIQUE, 0,op,"Undefined button.");
return 1;
}
}
abort(); /* eanders: claim is this should never be reached;
if it is, look at 91.5 code to determine what the value of
repeat was supposed to be and make sure you don't reach here. */
}
int receive_play_again(object *op, char key)
{
if(key=='q'||key=='Q') {
remove_friendly_object(op);
leave(op->contr);
return 2;
}
else if(key=='a'||key=='A') {
object *tmp;
remove_friendly_object(op);
op->contr->ob=get_player(op->contr,op->map);
tmp=op->contr->ob;
add_friendly_object(tmp);
tmp->contr->password[0]='~';
if(tmp->name!=NULL)
free_string(tmp->name);
get_name(tmp);
add_refcount(tmp->name = op->name);
op->type=DEAD_OBJECT;
free_object(op);
op=tmp;
}
return 0;
}
/* splitting this out produced a 250 line function !!!!! */
/* return 1 = repeat, return 0 = don't repeat, return 2 = immediate return */
/* needs keycode, gkey, char (text[0]) */
int handle_keypress(object *op,unsigned int keycode,KeySym keysym,char key)
{
op->contr->count_left=0;
switch(op->contr->state) {
case ST_PLAYING:
return parse_key(op,key,keycode,
keysym);
case ST_PLAY_AGAIN:
return receive_play_again(op, key);
case ST_ROLL_STAT:
return key_roll_stat(op, key);
case ST_CHANGE_CLASS:
return key_change_class(op, key);
case ST_CONFIRM_QUIT:
return key_confirm_quit(op, key);
case ST_CONFIGURE:
configure_keys(op, keycode, keysym);
return 1;
case ST_GET_NAME: /* Waiting for player name */
receive_player_name(op,key);
return 1;
case ST_GET_PASSWORD: /* Waiting for player password */
case ST_CONFIRM_PASSWORD: /* Confirm new password */
receive_player_password(op,key);
return 1;
case ST_MENU_MORE: /* more items to be listed */
shop_listing_more(op);
return 0;
#ifdef SIMPLE_PARTY_SYSTEM
case ST_GET_PARTY_PASSWORD: /* Get password for party */
receive_party_password(op,key);
return 1;
#endif /* SIMPLE_PARTY_SYSTEM */
default:
LOG(llevError,"Illegal state: %d\n",op->contr->state);
return 0;
}
abort(); /* eanders: claim is this should never be reached;
if it is, look at 91.5 code to determine what the value of repeat
was supposed to be and return the right thing. */
}
/* This is similar to handle_player, below, but is only used by the
* new client/server stuff.
* This is sort of special, in that the new client/server actually uses
* the new speed values for commands.
*
* Returns true if there are more actions we can do.
*/
int handle_newcs_player(object *op)
{
if(op->contr->state == ST_PLAYING && op->contr->loading != NULL) {
if(op->contr->loading->in_memory == MAP_IN_MEMORY) {
LOG(llevDebug,"In handle player, entering map\n");
enter_map(op);
}
else
return 0;
}
if(op->invisible&&!(QUERY_FLAG(op,FLAG_MAKE_INVIS))) {
op->invisible--;
if(!op->invisible) {
CLEAR_FLAG(op, FLAG_UNDEAD);
update_object(op);
}
}
draw_stats(op);
if(op->direction) {
/* All move commands take 1 tick, at least for now */
op->speed_left--;
if(op->contr->berzerk&&(op->contr->braced||(!op->contr->fire_on)))
move_player(op,op->direction);
else if(!move_ob(op,op->direction)||!check_pick(op))
op->direction=0;
if (op->speed_left>0) return 1;
else return 0;
}
return 0;
}
void handle_player(object *op) {
int repeat,windex=0;
char text[10];
Window win_clr[10];
if(op->contr->state == ST_PLAYING && op->contr->loading != NULL) {
op->speed_left++;
if(op->contr->loading->in_memory == MAP_IN_MEMORY) {
LOG(llevDebug,"In handle player, entering map\n");
enter_map(op);
}
else
return;
}
if(op->invisible&&!(QUERY_FLAG(op,FLAG_MAKE_INVIS))) {
op->invisible--;
if(!op->invisible) {
CLEAR_FLAG(op, FLAG_UNDEAD);
update_object(op);
}
}
op->contr->has_hit=0;
draw_stats(op);
if (op->contr->eric_server>0) {
LOG(llevError,"new client/server player called handle_player\n");
return;
}
repeat=1;
while(repeat) {
if (QUERY_FLAG(op,FLAG_SCARED)) {
flee_player(op);
return;
}
repeat=0;
if (XEventsQueued(op->contr->gdisp, QueuedAfterReading)==0) {
if(op->contr->count_left>0) {
int tmp_fire_on=op->contr->fire_on;
op->contr->fire_on=op->contr->prev_fire_on;
op->contr->count_left--;
parse_key(op, op->contr->prev_cmd,
op->contr->prev_keycode, op->contr->prev_keysym);
op->contr->fire_on=tmp_fire_on;
} else if(op->direction)
if(op->contr->berzerk&&(op->contr->braced||(!op->contr->fire_on)))
move_player(op,op->direction);
else if(!move_ob(op,op->direction)||!check_pick(op))
op->direction=0;
return;
}
op->contr->idle=0;
XNextEvent(op->contr->gdisp,&op->contr->gevent);
switch(op->contr->gevent.type) {
case ConfigureNotify:
if(op->contr->gevent.xconfigure.window==op->contr->win_info)
resize_win_info(op->contr,&op->contr->gevent);
else if(op->contr->gevent.xconfigure.window==op->contr->win_inv)
resize_win_inv(op->contr,&op->contr->gevent);
else if(op->contr->gevent.xconfigure.window==op->contr->win_look)
resize_win_look(op->contr,&op->contr->gevent);
break;
case Expose:
repeat=1;
if(has_cleared_window(op->contr->gevent.xexpose.window,win_clr,windex))
break;
win_clr[windex++]=op->contr->gevent.xexpose.window;
if(op->contr->gevent.xexpose.window==op->contr->win_stats) {
XClearWindow(op->contr->gdisp,op->contr->win_stats);
op->contr->last_value= -1;
draw_stats(op);
} else if(op->contr->gevent.xexpose.window==op->contr->win_info)
draw_all_info(op);
else if(op->contr->gevent.xexpose.window==op->contr->win_inv)
draw_all_inventory(op);
else if(op->contr->gevent.xexpose.window==op->contr->win_look)
draw_all_look(op);
else if(op->contr->gevent.xexpose.window==op->contr->win_message)
draw_all_message(op);
else if(op->contr->gevent.xexpose.window==op->contr->win_game)
refresh(op);
else if(!op->contr->split_window&&
op->contr->gevent.xexpose.window==op->contr->win_root) {
XClearWindow(op->contr->gdisp,op->contr->win_root);
}
op->speed_left++;
break;
case MappingNotify:
op->speed_left++;
XRefreshKeyboardMapping(&op->contr->gevent.xmapping);
repeat=1;
break;
case ClientMessage:
cmev = (XClientMessageEvent *) & op->contr->gevent;
if (cmev->message_type == Protocol_atom && cmev->data.l[0] == Kill_atom) {
LOG(llevDebug,"Got WM_DELETE_WINDOW from %s.\n",op->name);
if (op->map->in_memory == MAP_IN_MEMORY)
{
if (!QUERY_FLAG(op,FLAG_REMOVED)) {
op->map->players--;
remove_ob(op);
}
}
op->contr->state = 1;
op->direction = 0;
op->contr->count_left = 0;
new_draw_info_format(NDI_UNIQUE|NDI_ALL, 0, NULL,
"%s lost connection.", op->name);
strcpy(op->contr->killer, "lost connection");
check_score(op);
(void) save_player(op, 0);
remove_lock(op->contr);
free_player(op->contr);
if(first_player==NULL) {
if (server_mode != SERVER_ENABLED)
exit(0);
XCloseDisplay(op->contr->gdisp);
LOG(llevDebug, "In server mode: continuing action.\n");
}
#if 0 /* This should crash if it doesn't return! -Frank */
if (first_player==NULL)
#endif /* (Also, free_player() must be before the NULL test!) */
return;
}
break;
case ButtonPress:
repeat = handle_buttonpress(op);
break;
case KeyRelease:
repeat=1;
XLookupString(&op->contr->gevent.xkey,text,10,&op->contr->gkey,NULL);
parse_key_release(op);
break;
case KeyPress:
if(!XLookupString(&op->contr->gevent.xkey,text,10,
&op->contr->gkey,NULL)) {
text[0]='\0';
}
repeat = handle_keypress(op,op->contr->gevent.xkey.keycode,
op->contr->gkey,text[0]);
if (repeat==2)
return;
break;
default:
repeat=1;
}
}
if (!op->contr->keyboard_flush) return;
/* This should work better at removing keystrokes from the Queue
* than the old method. The old method could only remove keystrokes if
* they where the first events. But, if the queue was keyboard,
* keyboard, window, keyboard, it would stop at the window event,
* and one keyboard event would still be around. The following should
* remove all wanted events, and nothing more.
*/
while(XCheckMaskEvent(op->contr->gdisp, KeyPressMask | KeyReleaseMask | ButtonPressMask, &op->contr->gevent)) {
switch(op->contr->gevent.type) {
case KeyPress:
XLookupString(&op->contr->gevent.xkey,text,10,&op->contr->gkey,NULL);
/* Some keypresses we can't just throw away...: */
if((op->contr->gevent.xkey.keycode==op->contr->firekey[0] &&
op->contr->gkey==op->contr->firekeysym[0]) ||
(op->contr->gevent.xkey.keycode==op->contr->firekey[1] &&
op->contr->gkey==op->contr->firekeysym[1]))
op->contr->fire_on=1;
if((op->contr->gevent.xkey.keycode==op->contr->runkey[0] &&
op->contr->gkey==op->contr->runkeysym[0]) ||
(op->contr->gevent.xkey.keycode==op->contr->runkey[1] &&
op->contr->gkey==op->contr->runkeysym[1]))
op->contr->run_on=1;
/* LOG(llevDebug,"Through away key %s from input",text);*/
continue;
case KeyRelease:
XLookupString(&op->contr->gevent.xkey,text,10,&op->contr->gkey,NULL);
parse_key_release(op);
continue;
case ButtonPress:
continue;
default:
LOG(llevError,"While flushing keyboard, got unkown X event (%d)\n", op->contr->gevent.type);
break;
}
break;
}
}
object *esrv_getopfromcid(long client_id)
{
struct pl *l;
for(l = first_player;l!= NULL;l=l->next) {
if (l->eric_server == client_id)
break;
}
if (l==NULL) {
LOG(llevError,"Tried to get CID(%d) which doesn't exist.\n",client_id);
abort();
}
return l->ob;
}
object *esrv_get_ob_from_count(object *pl, long count)
{
object *op, *tmp;
if (pl->count == count)
return pl;
for(op = pl->inv; op; op = op->below)
if (op->count == count)
return op;
else if (op->type == CONTAINER && pl->container == op)
for(tmp = op->inv; tmp; tmp = tmp->below)
if (tmp->count == count)
return tmp;
for(op = get_map_ob (pl->map, pl->x, pl->y); op; op = op->above)
if (op->count == count)
return op;
else if (op->type == CONTAINER && pl->container == op)
for(tmp = op->inv; tmp; tmp = tmp->below)
if (tmp->count == count)
return tmp;
return NULL;
}
void esrv_examine_object (object *pl, long tag)
{
object *op = esrv_get_ob_from_count(pl, tag);
if (!op) {
LOG(llevDebug, "Player '%s' tried examine the unknown object (%d)\n",
pl->name, tag);
return;
}
examine (pl, op);
}
void esrv_apply_object (object *pl, long tag)
{
object *op = esrv_get_ob_from_count(pl, tag);
if (!op) {
LOG(llevDebug, "Player '%s' tried apply the unknown object (%d)\n",
pl->name, tag);
return;
}
apply (pl, op);
}
void esrv_move_object (object *pl, long to, long tag, long nrof)
{
object *op, *env;
printf ("esrv_move_object:\n");
printf ("Trying to locate object %ld from player %s.\n", tag, pl->name);
op = esrv_get_ob_from_count(pl, tag);
if (!op) {
LOG(llevDebug, "Player '%s' tried to move the unknown object (%ld)\n",
pl->name, tag);
return;
}
printf ("and the object was '%s'.\n", op->name);
if (!to) { /* drop it to the ground */
printf ("Drop it on the ground.\n");
drop_object (pl, op, nrof);
return;
} else if (to == pl->count) { /* pick it up to the inventory */
/* later */
printf ("Pickup not impl. yet.\n");
pick_up_object (pl, pl, op, nrof);
return ;
}
printf ("It was not dropped or picked up, put it in sack (%ld).\n", to);
env = esrv_get_ob_from_count(pl, to);
if (!env) {
LOG(llevDebug,
"Player '%s' tried to move object to the unknown location (%d)\n",
pl->name, to);
return;
}
printf ("Sacks name was '%s'.\n", env->name);
put_object_in_sack (pl, env, op, nrof);
}
void esrv_quit_player(long client_id)
{
struct pl *l;
for(l = first_player;l!= NULL;l=l->next) {
if (l->eric_server == client_id)
break;
}
if (l!=NULL) {
if(!QUERY_FLAG(l->ob,FLAG_REMOVED)) {
terminate_all_pets(l->ob);
remove_ob(l->ob);
}
leave(l);
}
}
int save_life(object *op) {
object *tmp;
char buf[MAX_BUF];
if(!QUERY_FLAG(op,FLAG_LIFESAVE))
return 0;
for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
if(QUERY_FLAG(tmp, FLAG_APPLIED)&&QUERY_FLAG(tmp,FLAG_LIFESAVE)) {
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_OB_EVAPORATE);
#endif
sprintf(buf,"Your %s vibrates violently, then evaporates.",
query_name(tmp));
new_draw_info(NDI_UNIQUE, 0,op,buf);
remove_ob(tmp);
free_object(tmp);
CLEAR_FLAG(op, FLAG_LIFESAVE);
if(op->stats.hp<0)
op->stats.hp = op->stats.maxhp;
if(op->stats.food<0)
op->stats.food = 999;
return 1;
}
LOG(llevError,"Error: LIFESAVE set without applied object.\n");
CLEAR_FLAG(op, FLAG_LIFESAVE);
return 0;
}
/* This goes throws the inventory and removes unpaid objects, and puts them
* back in the map (location and map determined by values of env). This
* function will descend into containers. op is the object to start the search
* from.
*/
void remove_unpaid_objects(object *op, object *env)
{
object *next;
while (op) {
next=op->below; /* Make sure we have a good value, in case
* we remove object 'op'
*/
if (QUERY_FLAG(op, FLAG_UNPAID)) {
remove_ob(op);
op->x = env->x;
op->y = env->y;
insert_ob_in_map(op, env->map);
}
else if (op->inv) remove_unpaid_objects(op->inv, env);
op=next;
}
}
void do_some_living(object *op) {
char buf[MAX_BUF];
object *tmp;
int last_food=op->stats.food;
int gen_hp, gen_sp, gen_grace;
int x,y,i; /* these are for resurrection */
mapstruct *map; /* this is for resurrection */
if (op->contr->outputs_sync) {
for (i=0; i<NUM_OUTPUT_BUFS; i++)
if (op->contr->outputs[i].buf!=NULL &&
(op->contr->outputs[i].first_update+op->contr->outputs_sync)<pticks)
flush_output_element(op, &op->contr->outputs[i]);
}
if(op->contr->state==ST_PLAYING) {
gen_hp=(op->contr->gen_hp+1)*op->stats.maxhp;
gen_sp=(op->contr->gen_sp+1)*op->stats.maxsp;
gen_grace=(op->contr->gen_grace+1)*op->stats.maxgrace;
if(op->contr->golem==NULL&&--op->last_sp<0) {
if(op->stats.sp<op->stats.maxsp)
op->stats.sp++,op->stats.food--;
op->last_sp=2000/(gen_sp<20 ? 30 : gen_sp+10);
for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
if((tmp->type==ARMOUR||tmp->type==HELMET||tmp->type==BOOTS||
tmp->type==SHIELD||tmp->type==GLOVES||tmp->type==GIRDLE||
tmp->type==AMULET)
&&QUERY_FLAG(tmp, FLAG_APPLIED))
op->last_sp+=ARMOUR_SPELLS(tmp);
}
/* regenerate grace */
/* I altered this a little - maximum grace is ony achieved through prayer -b.t.*/
if(--op->last_grace<0) {
#ifndef ALLOW_SKILLS /* allow regen 'naturally' to only 1/2 maxgrace w/ skills code */
if(op->stats.grace<op->stats.maxgrace)
#else
if(op->stats.grace<op->stats.maxgrace/2)
#endif
op->stats.grace++; /* no penalty in food for regaining grace */
op->last_grace=2500/(gen_grace<20? 30:gen_grace+10);
/* wearing stuff doesn't detract from grace generation. */
}
if(--op->last_heal<0) {
if(op->stats.hp<op->stats.maxhp)
op->stats.hp++,op->stats.food--;
op->last_heal=1200/(gen_hp<20 ? 30 : gen_hp+10);
if(op->contr->digestion<0)
op->stats.food+=op->contr->digestion;
else if(op->contr->digestion>0&&RANDOM()%(1+op->contr->digestion))
op->stats.food=last_food;
}
if(--op->last_eat<0) {
int bonus=op->contr->digestion>0?op->contr->digestion:0,
penalty=op->contr->digestion<0?-op->contr->digestion:0;
op->last_eat=25*(1+bonus)/(op->contr->gen_hp+penalty+1);
op->stats.food--;
}
}
if(op->contr->state==ST_PLAYING&&op->stats.food<0&&op->stats.hp>=0) {
object *tmp;
for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
if(!QUERY_FLAG(tmp, FLAG_UNPAID)&&
(tmp->type==FOOD||tmp->type==DRINK||tmp->type==POISON))
{
new_draw_info(NDI_UNIQUE, 0,op,"You blindly grab for a bite of food.");
apply(op,tmp);
if(op->stats.food>=0||op->stats.hp<0)
break;
}
}
while(op->stats.food<0&&op->stats.hp>0)
op->stats.food++,op->stats.hp--;
if (!op->contr->state&&!QUERY_FLAG(op,FLAG_WIZ)&&(op->stats.hp<0||op->stats.food<0)) {
if(save_life(op))
return;
if(op->stats.food<0) {
#ifdef EXPLORE_MODE
if (op->contr->explore) {
new_draw_info(NDI_UNIQUE, 0,op,"You would have starved, but you are");
new_draw_info(NDI_UNIQUE, 0,op,"in explore mode, so...");
op->stats.food=999;
return;
}
#endif /* EXPLORE_MODE */
sprintf(buf,"%s starved to death.",op->name);
strcpy(op->contr->killer,"starvation");
}
else
#ifdef EXPLORE_MODE
if (op->contr->explore) {
new_draw_info(NDI_UNIQUE, 0,op,"You would have died, but you are");
new_draw_info(NDI_UNIQUE, 0,op,"in explore mode, so...");
op->stats.hp=op->stats.maxhp;
return;
}
#endif /* EXPLORE_MODE */
sprintf(buf,"%s died.",op->name);
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_PLAYER_DIES);
#endif
/* save the map location for corpse, gravestone*/
x=op->x;y=op->y;map=op->map;
#ifdef NOT_PERMADEATH
/****************************************************************************/
/* Patch: NOT_PERMADEATH Author: Charles Henrich */
/* Email: henrich@crh.cl.msu.edu Date : April 9, 1993 */
/* */
/* Purpose: This patch changes death from being a permanent, very painful */
/* thing, to a painful, but survivable thing. More mudlike in */
/* nature. With this patch defined, when a player dies, they will */
/* permanently lose one point off of a random stat, as well as */
/* losing 20% of their experience points. Then they are whisked */
/* to the start map. Although this sounds very nice here, it is */
/* still REAL painful to die, 20% of a million is alot! */
/* */
/****************************************************************************/
/**************************************/
/* */
/* Pick a stat, and steal on pt from */
/* it... */
/* */
/**************************************/
i = RANDOM() % 7;
change_attr_value(&(op->stats), i,-1);
check_stat_bounds(&(op->stats));
change_attr_value(&(op->contr->orig_stats), i,-1);
check_stat_bounds(&(op->contr->orig_stats));
new_draw_info(NDI_UNIQUE, 0,op, lose_msg[i]);
/**************************************/
/* */
/* Lets make up a gravestone to put */
/* here... We put experience lost on */
/* it for kicks.... */
/* */
/**************************************/
tmp=arch_to_object(find_archetype("gravestone"));
sprintf(buf,"%s's gravestone",op->name);
tmp->name=add_string(buf);
sprintf(buf,"RIP\nHere rests the hero %s the %s,\n"
"who lost %d experience when killed\n"
"by %s.\n",
op->name, op->contr->title, (int)(op->stats.exp * 0.20),
op->contr->killer);
tmp->msg = add_string(buf);
tmp->x=op->x,tmp->y=op->y;
insert_ob_in_map(tmp,op->map);
/**************************************/
/* */
/* Subtract the experience points, */
/* if we died cause of food, give us */
/* food, and reset HP's... */
/* */
/**************************************/
/* remove any poisoning and confusion the character may be suffering. */
cast_heal(op, 0, SP_CURE_POISON);
cast_heal(op, 0, SP_CURE_CONFUSION);
add_exp(op, (op->stats.exp * -0.20));
if(op->stats.food < 0) op->stats.food = 500;
op->stats.hp = op->stats.maxhp;
/*
* Check to see if the player is in a shop. IF so, then check to see if
* the player has any unpaid items. If so, remove them and put them back
* in the map.
*/
tmp= get_map_ob(op->map, op->x, op->y);
if (tmp && tmp->type == SHOP_FLOOR) {
remove_unpaid_objects(op->inv, op);
}
/**************************************/
/* */
/* Move the player to the beginning */
/* map.... */
/* */
/**************************************/
tmp=get_object();
EXIT_PATH(tmp) = add_string(first_map_path);
enter_exit(op,tmp);
/* commenting this out seems to fix core dumps on some systems. */
free_object(tmp);
/**************************************/
/* */
/* Repaint the characters inv, and */
/* stats, and show a nasty message ;) */
/* */
/**************************************/
draw_stats(op);
/* draw_inventory(op); inventory hasn't changed */
new_draw_info(NDI_UNIQUE, 0,op,"YOU HAVE DIED.");
save_player(op,1);
return;
#endif
/* If NOT_PERMADETH is set, then the rest of this is not reachable. This
* should probably be embedded in an else statement.
*/
#ifdef SIMPLE_PARTY_SYSTEM
op->contr->party_number=(-1);
#endif /* SIMPLE_PARTY_SYSTEM */
#ifdef SET_TITLE
op->contr->own_title[0]='\0';
#endif /* SET_TITLE */
op->contr->count_left=0;
load_default_keys(op->contr);
new_draw_info(NDI_UNIQUE|NDI_ALL, 0,NULL, buf);
check_score(op);
if(op->contr->golem!=NULL) {
remove_friendly_object(op->contr->golem);
remove_ob(op->contr->golem);
free_object(op->contr->golem);
op->contr->golem=NULL;
}
op->contr->freeze_inv=1;
op->contr->freeze_look=1;
loot_object(op); /* Remove some of the items for good */
op->contr->freeze_inv=0;
op->contr->freeze_look=0;
remove_ob(op);
op->direction=0;
if(!QUERY_FLAG(op,FLAG_WAS_WIZ)&&op->stats.exp) {
delete_character(op->name);
#ifndef NOT_PERMADEATH
#ifdef RESURRECTION
/* save playerfile sans equipment when player dies
** then save it as player.pl.dead so that future resurrection
** type spells will work on them nicely
*/
op->stats.hp = op->stats.maxhp;
op->stats.food = 999;
/* set the location of where the person will reappear when */
/* maybe resurrection code should fix map also */
strcpy(op->contr->maplevel, EMERGENCY_MAPPATH);
if(op->map!=NULL)
op->map = NULL;
op->x = EMERGENCY_X;
op->y = EMERGENCY_Y;
save_player(op,0);
op->map = map;
/* please see resurrection.c: peterm */
dead_player(op);
#endif
#endif
}
play_again(op);
#ifdef NOT_PERMADEATH
tmp=arch_to_object(find_archetype("gravestone"));
sprintf(buf,"%s's gravestone",op->name);
tmp->name=add_string(buf);
sprintf(buf,"RIP\nHere rests the hero %s the %s,\nwho was killed by %s.\n",
op->name, op->contr->title, op->contr->killer);
tmp->msg = add_string(buf);
tmp->x=x,tmp->y=y;
insert_ob_in_map(tmp,map);
#else
/* peterm: added to create a corpse at deathsite. */
tmp=arch_to_object(find_archetype("corpse_pl"));
sprintf(buf,"%s", op->name);
if (tmp->name)
free_string (tmp->name);
tmp->name=add_string(buf);
tmp->level=op->level;
tmp->x=x;tmp->y=y;
if (tmp->msg)
free_string(tmp->msg);
tmp->msg = gravestone_text(op);
SET_FLAG (tmp, FLAG_UNIQUE);
insert_ob_in_map(tmp,map);
#endif
}
}
void loot_object(object *op) { /* Grab and destroy some treasure */
object *tmp,*tmp2,*next;
if (op->container) { /* close open sack first */
if (op->contr->eric_server > 0)
esrv_apply_container (op, op->container);
else
apply_container (op, op->container);
}
for(tmp=op->inv;tmp!=NULL;tmp=next) {
next=tmp->below;
remove_ob(tmp);
tmp->x=op->x,tmp->y=op->y;
if (tmp->type == CONTAINER) { /* empty container to ground */
loot_object(tmp);
}
if(!QUERY_FLAG(tmp, FLAG_UNIQUE) && (QUERY_FLAG(tmp, FLAG_STARTEQUIP)
|| QUERY_FLAG(tmp,FLAG_NO_DROP) || !(RANDOM()%3))) {
if(tmp->nrof>1) {
tmp2=get_split_ob(tmp,1+RANDOM()%(tmp->nrof-1));
free_object(tmp2);
insert_ob_in_map(tmp,op->map);
} else
free_object(tmp);
} else
insert_ob_in_map(tmp,op->map);
}
}
/*
* fix_weight(): Check recursively the weight of all players, and fix
* what needs to be fixed. Refresh windows and fix speed if anything
* was changed.
*/
void fix_weight() {
player *pl;
for (pl = first_player; pl != NULL; pl = pl->next) {
int old = pl->ob->carrying, sum = sum_weight(pl->ob);
if(old == sum)
continue;
fix_player(pl->ob);
draw_inventory(pl->ob);
LOG(llevDebug,"Fixed inventory in %s (%d -> %d)\n",
pl->ob->name, old, sum);
}
}
void fix_luck() {
player *pl;
for (pl = first_player; pl != NULL; pl = pl->next)
if (!pl->ob->contr->state)
change_luck(pl->ob, 0);
}
void client_speed(long cid)
{
player *pl;
for(pl=first_player;pl != NULL; pl = pl->next)
if (pl->eric_server == cid)
printf("speed left = %.2f\n",pl->ob->speed_left);
}